package com.lineage.server.clientpackets;

import com.lineage.config.ConfigOther;
import com.lineage.server.datatables.RecordTable;
import com.lineage.server.datatables.SprTable;
import com.lineage.server.datatables.lock.IpReading;
import com.lineage.server.model.Instance.L1PcInstance;
import com.lineage.server.model.L1Teleport;
import com.lineage.server.serverpackets.S_Disconnect;
import com.lineage.server.serverpackets.S_Paralysis;
import com.lineage.server.serverpackets.S_ServerMessage;
import com.lineage.server.serverpackets.S_SystemMessage;
import com.lineage.server.serverpackets.S_ToGmMessage;
import com.lineage.server.serverpackets.ServerBasePacket;
import com.lineage.server.utils.log.PlayerLogUtil;
import com.lineage.server.world.World;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Date;
import java.util.EnumMap;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class AcceleratorChecker {
  public static final int R_OK = 0;
  
  public static final int R_DETECTED = 1;
  
  public static final int R_DISPOSED = 2;
  
  private static final Log _log = LogFactory.getLog(AcceleratorChecker.class);
  
  private static final int INJUSTICE_COUNT_LIMIT = 10;
  
  private static final int JUSTICE_COUNT_LIMIT = 4;
  
  private static final double HASTE_RATE = 0.75D;
  
  private static final double WAFFLE_RATE = 0.87D;
  
  private static final double DOUBLE_HASTE_RATE = 0.375D;
  
  private static final double STANDARD_RATE = 1.0D;
  
  public static final int R_DISCONNECTED = 2;// 連續異常
  
  public static double CHECK_STRICTNESS = ConfigOther.SPEED_TIME;
  
  private final L1PcInstance _pc;
  
  private final EnumMap<ACT_TYPE, Long> _actTimers;
  
  private final EnumMap<ACT_TYPE, Long> _checkTimers;
  
  private int _injusticeCount;
  
  private int _justiceCount;
  
  public AcceleratorChecker(L1PcInstance pc) {
    this._actTimers = new EnumMap<>(ACT_TYPE.class);
    this._checkTimers = new EnumMap<>(ACT_TYPE.class);
    this._pc = pc;
    this._injusticeCount = 0;
    this._justiceCount = 0;
    long now = System.currentTimeMillis();
    ACT_TYPE[] values;
    int length = (values = ACT_TYPE.values()).length;
    int i = 0;
    while (i < length) {
      ACT_TYPE each = values[i];
      this._actTimers.put(each, Long.valueOf(now));
      this._checkTimers.put(each, Long.valueOf(now));
      i++;
    } 
  }
  
  private void toGmErrMsg(String name, int count) {
    try {
      if (count >= 5) {
        Collection<L1PcInstance> allPc = World.get().getAllPlayers();
        Iterator<L1PcInstance> iterator = allPc.iterator();
        while (iterator.hasNext()) {
          L1PcInstance tgpc = iterator.next();
          if (tgpc.isGm())
            tgpc.sendPackets((ServerBasePacket)new S_ToGmMessage("人物:" + name + " 速度异常!(" + count + "次)")); 
        } 
      } 
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } 
  }
  
  private void toGmKickMsg(String name) {
    try {
      Collection<L1PcInstance> allPc = World.get().getAllPlayers();
      Iterator<L1PcInstance> iterator = allPc.iterator();
      while (iterator.hasNext()) {
        L1PcInstance tgpc = iterator.next();
        if (tgpc.isGm())
          tgpc.sendPackets((ServerBasePacket)new S_ToGmMessage("人物:" + name + " 速度异常断线!")); 
      } 
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } 
  }
  
  public int checkInterval(ACT_TYPE type) {
    if (!ConfigOther.SPEED)
      return 0; 
    int result = 0;
    long now = System.currentTimeMillis();
    long interval = now - ((Long)this._actTimers.get(type)).longValue();
    int rightInterval = getRightInterval(type);
    LocalDateTime localNow = LocalDateTime.now();
    Duration duration = null;
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    StringBuilder actionResult = new StringBuilder(String.valueOf(dateFormat.format(new Date())) + " ");
    long _moverightInterval = getRightInterval(type);
    boolean errerCheck = false;
    switch (type) {
      case ATTACK:
        if (this._pc.getLastTimeAttackTime() == null) {
          this._pc.setLastTimeAttackTime(localNow);
          break;
        } 
        duration = Duration.between(this._pc.getLastTimeAttackTime(), localNow);
        if (duration.toMillis() * CHECK_STRICTNESS < _moverightInterval) {
          actionResult.append(String.format("玩家:%s :: 攻击时间应该要有%s毫秒，但实际跑了%s毫秒;\n\r", new Object[] { this._pc.getName(), Long.valueOf(_moverightInterval), Long.valueOf(duration.toMillis()) }));
          errerCheck = true;
        } 
        this._pc.setLastTimeAttackTime(localNow);
        break;
      case MOVE:
        if (this._pc.getLastTimeMoveTime() == null) {
          this._pc.setLastTimeMoveTime(localNow);
          break;
        } 
        duration = Duration.between(this._pc.getLastTimeMoveTime(), localNow);
        if (duration.toMillis() * CHECK_STRICTNESS < _moverightInterval) {
          actionResult.append(String.format("玩家:%s :: 移动时间应该要有%s毫秒，但实际跑了%s毫秒;\n\r", new Object[] { this._pc.getName(), Long.valueOf(_moverightInterval), Long.valueOf(duration.toMillis()) }));
          errerCheck = true;
        } 
        this._pc.setLastTimeMoveTime(localNow);
        break;
      case SPELL_DIR:
        if (this._pc.getLastTimeSpellDirTime() == null) {
          this._pc.setLastTimeSpellDirTime(localNow);
          break;
        } 
        duration = Duration.between(this._pc.getLastTimeSpellDirTime(), localNow);
        if (duration.toMillis() * CHECK_STRICTNESS < _moverightInterval) {
          actionResult.append(String.format("玩家:%s :: 有施法动作时间应该要有%s毫秒，但实际跑了%s毫秒;\n\r", new Object[] { this._pc.getName(), Long.valueOf(_moverightInterval), Long.valueOf(duration.toMillis()) }));
          errerCheck = true;
        } 
        this._pc.setLastTimeSpellDirTime(localNow);
        break;
      case SPELL_NODIR:
        if (this._pc.getLastTimeSpellNoirTime() == null) {
          this._pc.setLastTimeSpellNoirTime(localNow);
          break;
        } 
        duration = Duration.between(this._pc.getLastTimeSpellNoirTime(), localNow);
        if (duration.toMillis() * CHECK_STRICTNESS < _moverightInterval) {
          actionResult.append(String.format("玩家:%s :: 无施法动作时间应该要有%s毫秒，但实际跑了%s毫秒;\n\r", new Object[] { this._pc.getName(), Long.valueOf(_moverightInterval), Long.valueOf(duration.toMillis()) }));
          errerCheck = true;
        } 
        this._pc.setLastTimeSpellNoirTime(localNow);
        break;
      default:
        return 0;
    } 
    if (errerCheck) {
      this._injusticeCount++;
      PlayerLogUtil.writeLog("[加速侦测结果]", actionResult.toString());
      if (this._injusticeCount < 10) {
        result = 1;
      } else {
        doPunishment(ConfigOther.ILLEGAL_SPEEDUP_PUNISHMENT);
        result = 2;
      } 
    } else {
      this._justiceCount++;
      if (this._justiceCount >= 10) {
        this._injusticeCount = 0;
        this._justiceCount = 0;
      } else {
        result = 1;
      } 
    } 
    return result;
  }
  
  private void doPunishment(int punishmaent) {
    if (!this._pc.isGm()) {
      StringBuilder name;
      int x = this._pc.getX();
      int y = this._pc.getY();
      int mapid = this._pc.getMapId();
      switch (punishmaent) {
        case 0:
          this._pc.setSpeedError(this._pc.getSpeedError() + 1);
          if (this._pc.getSpeedError() >= 10) {
            long time = -1702967296L;
            Timestamp UnbanTime = new Timestamp(System.currentTimeMillis() + -1702967296L);
            IpReading.get().setUnbanTime(UnbanTime);
            IpReading.get().add(this._pc.getAccountName(), "加速器检测 自动封锁帐号三十日");
            this._pc.saveInventory();
            RecordTable.get().r_speed(this._pc.getName(), "侦测异常(攻速or走速");
            this._pc.sendPackets((ServerBasePacket)new S_Disconnect());
            World.get().broadcastServerMessage("玩家 : " + this._pc.getName() + " 因当日加速器检测断线十次，已封锁其帐号三十日。");
            _log.info(String.format("玩家 : %s 因当日加速器检测断线十次，已封锁其帐号三十日。", new Object[] { this._pc.getName() }));
            break;
          } 
          this._pc.sendPackets((ServerBasePacket)new S_ServerMessage(945));
          this._pc.saveInventory();
          this._pc.sendPackets((ServerBasePacket)new S_Disconnect());
          name = new StringBuilder();
          name.append(this._pc.getName());
          toGmKickMsg(name.toString());
          RecordTable.get().r_speed(this._pc.getName(), "侦测异常(攻速or走速");
          World.get().broadcastServerMessage("玩家 : " + this._pc.getName() + " 因为检测到加速器，已强制切断其连线。");
          _log.info(String.format("因为检测到%s正在使用加速器，强制切断其连线。", new Object[] { this._pc.getName() }));
          break;
        case 1:
          this._pc.sendPackets((ServerBasePacket)new S_Paralysis(5, true));
          this._pc.sendPackets((ServerBasePacket)new S_ServerMessage("因为检测到加速器，惩罚晕眩10秒"));
          RecordTable.get().r_speed(this._pc.getName(), "侦测异常(攻速or走速");
          try {
            Thread.sleep(10000L);
          } catch (Exception e) {
            System.out.println(e.getLocalizedMessage());
          } 
          this._pc.sendPackets((ServerBasePacket)new S_Paralysis(5, false));
          break;
        case 2:
          L1Teleport.teleport(this._pc, 32746, 32740, (short)666, 5, false);
          this._pc.sendPackets((ServerBasePacket)new S_SystemMessage("因为你使用加速器，被传送到了地域，10秒后传回。"));
          RecordTable.get().r_speed(this._pc.getName(), "侦测异常(攻速or走速");
          try {
            Thread.sleep(10000L);
          } catch (Exception e) {
            System.out.println(e.getLocalizedMessage());
          } 
          L1Teleport.teleport(this._pc, x, y, (short)mapid, 5, false);
          break;
        case 3:
          L1Teleport.teleport(this._pc, 32737, 32796, (short)99, 5, false);
          this._pc.sendPackets((ServerBasePacket)new S_SystemMessage("因为你使用加速器，被传送到了GM房，30秒后传回。"));
          try {
            Thread.sleep(30000L);
          } catch (Exception e) {
            System.out.println(e.getLocalizedMessage());
          } 
          L1Teleport.teleport(this._pc, x, y, (short)mapid, 5, false);
          RecordTable.get().r_speed(this._pc.getName(), "侦测异常(攻速or走速");
          break;
        case 4:
          this._pc.sendPackets((ServerBasePacket)new S_Paralysis(6, true));
          this._pc.sendPackets((ServerBasePacket)new S_ServerMessage("因为检测到加速器，限制移动10秒"));
          RecordTable.get().r_speed(this._pc.getName(), "侦测异常(攻速or走速");
          try {
            Thread.sleep(10000L);
          } catch (Exception e) {
            System.out.println(e.getLocalizedMessage());
          } 
          this._pc.sendPackets((ServerBasePacket)new S_Paralysis(6, false));
          break;
      } 
    } else {
      this._pc.sendPackets((ServerBasePacket)new S_SystemMessage("游戏管理员在游戏中使用加速器检测中。"));
      this._injusticeCount = 0;
      this._justiceCount = 0;
    } 
  }
  
  public int getRightInterval(ACT_TYPE type) {
    int interval = 0;
    switch (type) {
      case ATTACK:
        interval = SprTable.get().getAttackSpeed(this._pc.getTempCharGfx(), this._pc.getCurrentWeapon() + 1);
        break;
      case MOVE:
        interval = SprTable.get().getMoveSpeed(this._pc.getTempCharGfx(), this._pc.getCurrentWeapon());
        break;
      case SPELL_DIR:
        interval = SprTable.get().getDirSpellSpeed(this._pc.getTempCharGfx());
        break;
      case SPELL_NODIR:
        interval = SprTable.get().getNodirSpellSpeed(this._pc.getTempCharGfx());
        break;
      default:
        return 0;
    } 
    switch (this._pc.getMoveSpeed()) {
      case 1:
        interval = (int)(interval * 0.75D);
        break;
      case 2:
        interval = (int)(interval / 0.75D);
        break;
    } 
    switch (this._pc.getBraveSpeed()) {
      case 1:
        interval = (int)(interval * 0.75D);
        break;
      case 3:
        interval = (int)(interval * 0.87D);
        break;
      case 4:
        if (type.equals(ACT_TYPE.MOVE) && this._pc.isFastMovable())
          interval = (int)(interval * 0.75D); 
        break;
      case 5:
        interval = (int)(interval * 0.375D);
        break;
      case 6:
        if (type.equals(ACT_TYPE.ATTACK))
          interval = (int)(interval * 0.75D); 
        break;
    } 
    if (this._pc.isBraveX())
      interval = (int)(interval * 0.87D); 
    if (this._pc.isWindShackle() && !type.equals(ACT_TYPE.MOVE))
      interval /= 2; 
    if (this._pc.getMapId() == 5143)
      interval = (int)(interval * 0.1D); 
    if (this._pc.hasSkillEffect(998))
      interval = (int)(interval * 0.87D); 
    return interval;
  }
  
	public static enum ACT_TYPE {
		MOVE, ATTACK, SPELL_DIR, SPELL_NODIR
	}
}
